//
// Created by DELL on 2022/4/19.
//
#pragma once

#include "../Utils.h"
#include "../VulkanDefine.h"

BEGIN_NS_VULKAN

struct SwapChainSupportDetails
{
    VkSurfaceCapabilitiesKHR capabilities;
    std::vector<VkSurfaceFormatKHR> formats;
    std::vector<VkPresentModeKHR> presentModes;
};

class Device
{
public:
    ~Device();
    Device(const Device&) = delete;
    Device operator=(const Device&) = delete;

    static DeviceRef create();

    VkCommandPool getCommandPool() const { return commandPool; }
    VkDevice getDevice() const { return device; }
    VkQueue getGraphicsQueue() const { return graphicsQueue; }
    VkQueue getPresentQueue() const { return presentQueue; }
    uint32_t getGraphicsQueueIndex() const { return graphicsQueueIndex; }
    uint32_t getPresentQueueIndex() const { return presentQueueIndex; }
    const VkPhysicalDeviceProperties& getDeviceProperties() const { return deviceProperties; }

    SwapChainSupportDetails getSwapChainSupport() { return querySwapChainSupport(physicalDevice); }
    uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
    VkFormat findSupportedFormat( const std::vector<VkFormat> &candidates, VkImageTiling tiling, VkFormatFeatureFlags features);

    VkCommandBuffer beginSingleTimeCommands();
    void endSingleTimeCommands(VkCommandBuffer commandBuffer);

    void createBuffer(VkDeviceSize          size,
                      VkBufferUsageFlags    usage,
                      VkMemoryPropertyFlags properties,
                      VkBuffer&             buffer,
                      VkDeviceMemory&       bufferMemory);
    void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);

    void createImageWithInfo(const VkImageCreateInfo& imageInfo,
                             VkMemoryPropertyFlags    properties,
                             VkImage&                 image,
                             VkDeviceMemory&          imageMemory);
    void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height, uint32_t layerCount);

private:
    Device();

    void pickPhysicalDevice();
    void createLogicalDevice();
    void createCommandPool();

    std::vector<const char*> device_extensions {VK_KHR_SWAPCHAIN_EXTENSION_NAME};

    bool isDeviceSuitable(VkPhysicalDevice device);
    bool findQueueFamilies(VkPhysicalDevice device);
    bool checkDeviceExtensionSupport(VkPhysicalDevice device);
    SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device);

    VkPhysicalDevice physicalDevice{};
    VkDevice device{};
    VkQueue graphicsQueue{};
    VkQueue presentQueue{};
    VkCommandPool commandPool{};

    uint32_t graphicsQueueIndex{};
    uint32_t presentQueueIndex{};
    VkPhysicalDeviceProperties deviceProperties{};
};

END_NS_VULKAN